package com.cise.searcher;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;

import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.store.FSDirectory;

import net.semanticmetadata.lire.DocumentBuilder;
import net.semanticmetadata.lire.ImageSearchHits;
import net.semanticmetadata.lire.ImageSearcher;

import com.cise.searcher.utils.Result;
import com.cise.sort.utils.AbstractResult;
import com.cise.utils.Util;

public class SearcherThread extends Thread {
	/**
	 * ����Ŀ��ͼƬ<br>
	 * ���û��ύ��ͼƬ
	 */
	private BufferedImage image;
	/**
	 * ������Ŀ¼
	 */
	private String rootIndexPath;
	/**
	 * ���߳����ڵ��̳߳�<br>
	 * �����̵߳����߳�Ҳ�������߳����ڵ��̳߳��й���
	 */
	private ExecutorService pool;
	/**
	 * ������ȡ�㷨��
	 */
	private String algorithmName;
	/**
	 * �����Y��
	 */
	private List<AbstractResult> results;
	/**
	 * �ص�����
	 */
	private CallBack callBack;
	/**
	 * ��������������<br>
	 * ���������̵߳ȴ����߳�ִ������ټ�������<br>
	 * �÷�����������ֱ���ڴ�������д Thread
	 */
	private CountDownLatch count;
	
	public SearcherThread(BufferedImage image, String rootIndexPath, 
			ExecutorService pool, String algorithmName, 
			CallBack callBack) {
		this.image = image;
		this.rootIndexPath = rootIndexPath;
		this.pool = pool;
		this.algorithmName = algorithmName;
		this.results = new ArrayList<AbstractResult>();
		this.callBack = callBack;
	}

	@Override
	public void run() {
		// ��������Ŀ¼�л�ȡ���е���Ŀ¼
		File file = new File(this.rootIndexPath);
		if(!file.exists()) {
			this.callBack.setResult(null);
			return;
		}
		File[] subFiles = file.listFiles();
		if(null == subFiles || subFiles.length == 0) {
			this.callBack.setResult(null);
			return;
		}
		this.count = new CountDownLatch(subFiles.length);
		System.out.println(this.algorithmName + " �߳̿�ʼʱ�䣺" + new Date(System.currentTimeMillis()));
		
		// Ϊÿ����Ŀ¼����һ���µĽ���ȥ��������
		for(int i = 0; i < subFiles.length; i++){
			final File f = subFiles[i];
			this.pool.execute(new Thread(){
				@Override
				public void run(){
					try {
						System.out.println("searching the index file : " + f.getAbsolutePath());
						System.out.println(this + " " + algorithmName + " " + f.getName() + " �߳̿�ʼʱ�䣺" + new Date(System.currentTimeMillis()));
						IndexReader ir = DirectoryReader.open(FSDirectory.open(f));
						ImageSearcher searcher = Util.getSearcherByAlgorithmName(algorithmName);
						ImageSearchHits hits = searcher.search(image, ir);
						for(int i = 0; i < hits.length(); i++){
							String fileName = hits.doc(i).getValues(
											DocumentBuilder.FIELD_NAME_IDENTIFIER)[0];
							synchronized (results) {
								results.add(new Result(hits.score(i),
										fileName));
							}
						}
					} catch (IOException e) {
						e.printStackTrace();
					} finally{
						System.out.println(algorithmName + " " + f.getName() + " �߳̽���ʱ�䣺" + new Date(System.currentTimeMillis()));
						count.countDown();
					}
				}
			});
		}
		
		// �ۺ������������
		try {
			this.count.await();
			// ����
			Collections.sort(results, new Comparator<AbstractResult>(){
				@Override
				public int compare(AbstractResult o1, AbstractResult o2) {
					double diff = o1.getDistance() - o2.getDistance();
					if(diff < 0) return -1;
					if(diff == 0) return 0;
					return 1;
				}
			});
			System.out.println(this + " " + algorithmName + " �߳̽���ʱ�䣺" + new Date(System.currentTimeMillis()));
			AbstractResult[] iResult = new AbstractResult[Util.EACH_SEARCHER_RESULT_NUM];
			for(int i = 0,j = 0; j < Util.EACH_SEARCHER_RESULT_NUM; i++){
				if(i > 0){
					if(iResult[j-1].getDistance() != results.get(i).getDistance()){
						iResult[j] = results.get(i);
						j++;
					}
				}
				else{
					iResult[i] = results.get(i);
					j++;
				}
			}
			this.callBack.setResult(iResult);
		} catch (InterruptedException e) {
			this.callBack.setResult(null);
			e.printStackTrace();
		}
	}
	
	/**
	 * �����̵߳Ļص�����<br>
	 * ���������������
	 * @author Mutear
	 *
	 */
	public interface CallBack{
		public void setResult(AbstractResult[] iResults);
	}

}
